﻿#include "QtWidgetsApplication4.h"

QtWidgetsApplication4::QtWidgetsApplication4(QWidget* parent)
	: QWidget(parent)
{
	ui.setupUi(this);

	paintEvent_init();                     //频谱图初始化

	QThread* thread = new QThread();       //创建线程
	AudioQThread* st = new AudioQThread(); //创建对象
	st->moveToThread(thread);              //将对象移动到线程中

	connect(thread, &QThread::started, st, &AudioQThread::work);
	connect(st, &AudioQThread::updatePlot, this, &QtWidgetsApplication4::update_chart);

	connect(ui.pushButton, &QPushButton::clicked, this, [=]() {
		thread->start(); //启动线程
		});
}

// 子线程，获取音频数据并执行FFT
void AudioQThread::work()
{
	// 使用默认音频格式
	formatAudio.setSampleRate(44100);
	formatAudio.setChannelCount(2);
	formatAudio.setSampleFormat(QAudioFormat::Int16);

	nyquistFreq = formatAudio.sampleRate() / 2.0;
	qInfo() << "采样率：" << formatAudio.sampleRate() << "Hz，奈奎斯特频率：" << nyquistFreq << "Hz";

	// 初始化FFT频率数组（映射FFT索引到实际频率）
	fftFreqs.resize(FFT_SIZE / 2);
	double freqStep = formatAudio.sampleRate() / (double)FFT_SIZE;  // FFT频率分辨率
	for (int i = 0; i < FFT_SIZE / 2; ++i) {
		fftFreqs[i] = i * freqStep;  // 第i个FFT点对应的实际频率
	}

	// 获取默认音频输入设备
	QAudioDevice inputDevice = QMediaDevices::defaultAudioInput();

	// 创建音频输入
	QAudioInput* audioInput = new QAudioInput(inputDevice, this);

	// 创建音频源
	QAudioSource* audioSource = new QAudioSource(inputDevice, formatAudio, this);

	//开始录音
	audioIODevice = audioSource->start();

	// 连接readyRead信号
	connect(audioIODevice, &QIODevice::readyRead, this, &AudioQThread::readAll);

	// 启动FFT和频段计算
	updateTimer->start();
}

// 转换为16位整数采样点，存入缓冲区（仅保留最新FFT_SIZE个点）
void AudioQThread::readAll()
{
	QByteArray data = audioIODevice->readAll();
	if (!data.isEmpty()) {
		const qint16* samples = reinterpret_cast<const qint16*>(data.constData());
		int sampleCount = data.size() / sizeof(qint16);
		for (int i = 0; i < sampleCount; ++i) {
			audioBuffer.append(samples[i]);
			if (audioBuffer.size() > FFT_SIZE) {
				audioBuffer.remove(0, audioBuffer.size() - FFT_SIZE);
			}
		}
	}
}

// 频谱处理、频段计算、通过updatePlot信号在UI中绘制频谱图
void AudioQThread::updateFFT()
{
	// 0、缓冲区数据不足，不执行FFT
	if (audioBuffer.size() < FFT_SIZE) return;

	// 1、 时域数据加汉宁窗（减少频谱泄漏）
	for (int i = 0; i < FFT_SIZE; ++i) {
		double hanning = 0.5 * (1 - cos(2 * M_PI * i / (FFT_SIZE - 1)));
		fftIn[i][0] = audioBuffer[i] * hanning;  // 实部（时域数据）
		fftIn[i][1] = 0.0;                       // 虚部（无）
	}

	// 2、 执行FFT，转换为频域数据
	fftw_execute(fftPlan);

	// 3、 FFT结果转换为分贝值（仅保留前半段，对应0Hz~奈奎斯特频率）
	fftDb.resize(FFT_SIZE / 2);
	for (int i = 0; i < FFT_SIZE / 2; ++i) {
		double magnitude = sqrt(fftOut[i][0] * fftOut[i][0] + fftOut[i][1] * fftOut[i][1]);
		fftDb[i] = (magnitude > 1e-8) ? 20 * log10(magnitude) : -120.0;  // 避免log(0)
	}

	// 4、 计算20个频段的平均能量（核心：按频段范围分组FFT数据）
	bandEnergy.fill(-60.0);  // 重置能量
	for (int bandIdx = 0; bandIdx < BAND_COUNT; ++bandIdx) {
		double bandStart = bandRanges[bandIdx].first;
		double bandEnd = bandRanges[bandIdx].second;
		double energySum = 0.0;
		int count = 0;

		// 遍历FFT频率点，找到当前频段内的所有点
		for (int fftIdx = 0; fftIdx < fftFreqs.size(); ++fftIdx) {
			double freq = fftFreqs[fftIdx];
			if (freq >= bandStart && freq < bandEnd) {
				energySum += fftDb[fftIdx];  // 累加该频段内的分贝值
				count++;
			}
		}

		// 计算该频段的平均能量（若有有效点）
		if (count > 0) {
			bandEnergy[bandIdx] = energySum / count;
		}
	}

	// 5、更新频谱图
	updatePlot(bandEnergy);
}

// 频谱图初始化
void QtWidgetsApplication4::paintEvent_init()
{
	// 1、创建图表布局
	QWidget* Widget = ui.widget;
	QVBoxLayout* layout = new QVBoxLayout(Widget);

	// 2、初始化柱状图数据集
	m_barSet = new QBarSet("FFT 分贝值");
	m_series = new QBarSeries();
	m_series->append(m_barSet);
	m_series->setBarWidth(1.0); // 相对宽度，0.0-1.0之间

	// 3、创建图表
	m_chart = new QChart();
	m_chart->addSeries(m_series);
	m_chart->setTitle("20频段音频频谱分析（20Hz-20kHz）");
	m_chart->setAnimationOptions(QChart::SeriesAnimations); // 添加动画效果
	m_chart->legend()->hide(); // 隐藏图例

	// 4、创建X轴标签
	for (int i = 1; i <= 20; ++i) {
		QString bandLabel = getBandLabel(i);  //获取x轴标签
		bandCategories << bandLabel;
		*m_barSet << 0.0;  //设置20个柱状图的默认值
	}

	// 5、创建X轴（频率点）
	m_axisX = new QBarCategoryAxis();
	m_chart->addAxis(m_axisX, Qt::AlignBottom);
	m_axisX->append(bandCategories); // 添加X轴标签
	m_series->attachAxis(m_axisX);
	m_axisX->setLabelsFont(QFont("Arial", 5)); // 设置字体和字号

	// 6、创建Y轴（分贝值）
	m_axisY = new QValueAxis();
	m_axisY->setTitleText("分贝 (dB)");
	m_axisY->setRange(0, 200); // 根据数据范围设置
	m_chart->addAxis(m_axisY, Qt::AlignLeft);
	m_series->attachAxis(m_axisY);

	// 7、创建图表视图
	m_chartView = new QChartView(m_chart);
	m_chartView->setRenderHint(QPainter::Antialiasing); // 抗锯齿

	// 8、将图表视图添加到布局中
	layout->setContentsMargins(0, 0, 0, 0);
	layout->addWidget(m_chartView);
}

QtWidgetsApplication4::~QtWidgetsApplication4()
{

}

// 子线程的构造中初始化音频处理线程
AudioQThread::AudioQThread()
{
	// 1、 初始化20频段范围（对数刻度，覆盖20Hz-20kHz，符合人耳听觉）
	const double bandStarts[] = { 20, 28, 40, 56, 80, 112, 160, 224, 320, 448,
								 640, 896, 1280, 1792, 2560, 3584, 5120, 7168, 10240, 14336 };
	const double bandEnds[] = { 28, 40, 56, 80, 112, 160, 224, 320, 448, 640,
							   896, 1280, 1792, 2560, 3584, 5120, 7168, 10240, 14336, 20000 };
	for (int i = 0; i < BAND_COUNT; ++i) {
		bandRanges.append({ bandStarts[i], bandEnds[i] });
	}
	bandEnergy.resize(BAND_COUNT, -60.0);  // 初始化能量为-60dB（噪声阈值）

	// 2、 初始化FFT资源（FFT_SIZE=2048，确保频率分辨率足够）
	fftIn = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * FFT_SIZE);
	fftOut = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * FFT_SIZE);
	fftPlan = fftw_plan_dft_1d(FFT_SIZE, fftIn, fftOut, FFTW_FORWARD, FFTW_ESTIMATE);

	// 3、 初始化刷新定时器（控制可视化帧率）
	updateTimer = new QTimer(this);
	updateTimer->setInterval(30);
	connect(updateTimer, &QTimer::timeout, this, &AudioQThread::updateFFT);

	// 4、 初始化音频缓冲区
	audioBuffer.reserve(FFT_SIZE);
}

AudioQThread::~AudioQThread()
{
}

void QtWidgetsApplication4::update_chart(QVector<double> bandEnergy)
{
	qDebug() << bandEnergy;

	// 批量更新数据
	for (int i = 0; i < bandEnergy.size(); ++i) {

		// 更新柱状图数据
		m_barSet->replace(i, bandEnergy[i]);
	}
}

// 自定义辅助函数：获取第i个频段的频率范围标签（1~20对应20Hz-20kHz的对数划分）
QString QtWidgetsApplication4::getBandLabel(int bandIndex)
{
	// 使用与AudioQThread中一致的频段范围定义
	const QVector<QString> labels = {
		"20-28Hz", "28-40Hz", "40-56Hz", "56-80Hz", "80-112Hz",
		"112-160Hz", "160-224Hz", "224-320Hz", "320-448Hz", "448-640Hz",
		"640-896Hz", "896-1.28kHz", "1.28-1.79kHz", "1.79-2.56kHz", "2.56-3.58kHz",
		"3.58-5.12kHz", "5.12-7.17kHz", "7.17-10.24kHz", "10.24-14.3kHz", "14.3-20kHz"
	};

	if (bandIndex >= 1 && bandIndex <= 20) {
		return labels[bandIndex - 1];
	}
	return QString("柱 %1").arg(bandIndex);
}
